package com.atguigu.gmall.order.controller;

import com.atguigu.gmall.cart.CartFeignClient;
import com.atguigu.gmall.common.constant.RabbitConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.AuthContextHolder;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.model.user.UserAddress;
import com.atguigu.gmall.order.service.api.OrderService;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.atguigu.gmall.user.UserFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;

/**
 * @author: 封捷
 * @create-date: 2022/9/7 10:57
 */
@RestController
@RequestMapping("/api/order")
public class OrderApiController {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private CartFeignClient cartFeignClient;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private OrderService orderService;

    @Value("${ware.url}")
    private String wareUrl;

    @Autowired
    private RabbitService rabbitService;

    /**
     * 生成流水号
     * @param userId
     * @return
     */
    private String generateTradeNo(String userId) {

        // 1、拼接流水号的 key
        String keyPrefix = "order:";
        String keySuffix = ":sequence";
        String orderSequenceKey = keyPrefix + userId + keySuffix;

        // 2、生成流水号的 value
        String orderSequenceValue = UUID.randomUUID().toString().replace("-", "").toUpperCase();

        // 3、存入 Redis
        redisTemplate.opsForValue().set(orderSequenceKey, orderSequenceValue);

        // 4、返回数据
        return orderSequenceValue;
    }

    /**
     * 校验流水号
     * @param userId
     * @param tradeNoFromPage
     * @return
     */
    private boolean checkTradeNo(String userId, String tradeNoFromPage) {

        // 1、拼接流水号的 key
        String keyPrefix = "order:";
        String keySuffix = ":sequence";
        String orderSequenceKey = keyPrefix + userId + keySuffix;

        // 2、根据流水号的 key 查询 Redis
        String orderSequenceValueFromRedis = (String) redisTemplate.opsForValue().get(orderSequenceKey);

        // 3、验证通过的条件：Redis 中存储的流水号存在、有效且和页面传过来的相等
        return (!StringUtils.isEmpty(orderSequenceValueFromRedis)) && orderSequenceValueFromRedis.equals(tradeNoFromPage);
    }

    /**
     * 删除流水号
     * @param userId
     */
    private void removeTradeNo(String userId) {
        // 1、拼接流水号的 key
        String keyPrefix = "order:";
        String keySuffix = ":sequence";
        String orderSequenceKey = keyPrefix + userId + keySuffix;

        // 2、执行删除
        redisTemplate.delete(orderSequenceKey);
    }

    // 访问地址中包含 auth，表示这个方法必须登录才能访问
    @GetMapping("/auth/trade")
    public Result<Map<String, Object>> trade(HttpServletRequest request) {

        // 1、获取 userId
        String userId = AuthContextHolder.getUserId(request);

        // 2、调用远程接口，获取用户地址
        List<UserAddress> userAddressList =
                userFeignClient.getUserAddressList(Long.parseLong(userId)).getData();

        // 3、调用远程接口，获取购物车中已经选中的购物项
        List<CartInfo> cartInfoList = cartFeignClient.getCheckedCartList(userId).getData();

        // 4、页面上需要的是 List<OrderDetail>，下面进行转换
        // [1]创建空集合
        List<OrderDetail> orderDetailList = new ArrayList<>();

        // [2]遍历 List<CartInfo> 集合，逐个转换
        for (CartInfo cartInfo : cartInfoList) {
            // [3]创建 OrderDetail 对象
            OrderDetail orderDetail = new OrderDetail();

            // [4]设置属性
            orderDetail.setSkuId(cartInfo.getSkuId());
            orderDetail.setSkuName(cartInfo.getSkuName());
            orderDetail.setImgUrl(cartInfo.getImgUrl());
            orderDetail.setOrderPrice(cartInfo.getSkuPrice());
            orderDetail.setSkuNum(cartInfo.getSkuNum());

            // [5]存入集合
            orderDetailList.add(orderDetail);
        }

        // 5、计算订单的总数量
        // [1]声明变量，保存总数量
        int totalNum = 0;

        // [2]遍历集合
        for (CartInfo cartInfo : cartInfoList) {
            // [3]获取每一个购物项的数量
            Integer skuNum = cartInfo.getSkuNum();

            // [4]累加
            totalNum += skuNum;
        }

        // 6、计算订单的总金额
        // [1]创建 OrderInfo 对象
        OrderInfo orderInfo = new OrderInfo();

        // [2]给 OrderInfo 对象装配 orderDetailList 集合
        orderInfo.setOrderDetailList(orderDetailList);

        // [3]计算总金额
        orderInfo.sumTotalAmount();

        // [4]读取总金额数值
        BigDecimal totalAmount = orderInfo.getTotalAmount();

        // 7、生成流水号
        String tradeNo = generateTradeNo(userId);

        // 8、保存数据
        // [1]创建保存数据的 Map 集合
        HashMap<String, Object> resultMap = new HashMap<>();

        // [2]存入 userAddressList
        resultMap.put("userAddressList", userAddressList);

        // [3]存入 orderDetailList
        resultMap.put("detailArrayList", orderDetailList);

        // [4]存入 totalNum
        resultMap.put("totalNum", totalNum);

        // [5]存入 totalAmount
        resultMap.put("totalAmount", totalAmount);

        // [6]存入流水号 tradeNo
        resultMap.put("tradeNo", tradeNo);

        // 9、返回汇总的数据
        return Result.ok(resultMap);
    }

    @PostMapping("/auth/submitOrder")
    public Result<Long> submitOrder(
            @RequestParam("tradeNo") String tradeNo, @RequestBody OrderInfo orderInfo, HttpServletRequest request) {

        // 1、检查流水号
        // [1]获取用户 id
        String userId = AuthContextHolder.getUserId(request);

        // [2]检查流水号
        boolean checkResult = checkTradeNo(userId, tradeNo);

        // [3]根据检查的结果给前端返回数据
        if (checkResult) {
            // [4]如果查验结果是真的，就应该从 Redis 中删除
            // ※因为如果查验结果为真，说明用户手里拿的是有效的流水号
            // 有效的流水号是一次性的，用完就删。
            removeTradeNo(userId);
        } else {
            // [5]如果查验的结果是假的，就说明发生了订单的重复提交
            // 所以后续操作中止，返回提示信息
            return Result.<Long>fail().message("流水号失效，请勿重复提交订单！");
        }

        // 2、遍历订单详情的集合数据，检查库存、价格
        // [1]获取订单详情数据集合
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (CollectionUtils.isEmpty(orderDetailList)) {
            // ※这种情况正常不应该出现，我们写上是以防万一
            return Result.<Long>fail().message("订单详情数据丢失，请确认后再提交！");
        }

        // [2]遍历集合
        for (OrderDetail orderDetail : orderDetailList) {

            Long skuId = orderDetail.getSkuId();
            String skuName = orderDetail.getSkuName();

            // [3]验证价格
            // ※读取当前订单中的价格
            BigDecimal orderPrice = orderDetail.getOrderPrice();

            // ※从数据库表 sku_info 中读取商品价格，这里是最权威的数据来源
            BigDecimal skuPrice = productFeignClient.getSkuPrice(skuId).getData();

            if (orderPrice == null || !orderPrice.equals(skuPrice)) {

                // 刷新购物车数据：确保 Redis 缓存中的购物项的价格是 skuPrice
                cartFeignClient.getCartListFromDBToCache(userId);

                // 返回提示消息
                return Result.<Long>fail().message(skuName + " 商品价格有变化，请确认后再提交！");
            }

            // [4]验证库存
            // ※特殊说明：访问库存系统无法使用 Feign 接口方式远程调用，而是需要使用 common-util 中封装的 HttpClientUtil
            // ※读取当前 skuId 对应的商品数量
            Integer skuNum = orderDetail.getSkuNum();

            // ※拼接访问库存系统的 URL 地址
            String wareSystemAccessPath = wareUrl + "/hasStock?skuId=" + skuId + "&num=" + skuNum;

            // ※调用 HttpClientUtil 的工具方法发送请求
            // ※返回值形式：0 表示库存不够；1 表示库存够
            String stockCheckResult = HttpClientUtil.doGet(wareSystemAccessPath);

            // ※判断库存检查结果
            if ("0".equals(stockCheckResult)) {
                // 返回提示消息
                return Result.<Long>fail().message(skuName + " 商品库存不足，请确认后再提交！");
            }

        }

        try {
            // 3、保存订单
            orderInfo.setUserId(Long.parseLong(userId));
            Long orderId = orderService.saveOrder(orderInfo, ProcessStatus.UNPAID);

            // ※附加功能：保存订单成功之后，发送延迟消息，在规定时间后检查订单是否支付
            rabbitService.sendDelayMessage(
                    RabbitConst.EXCHANGE_DIRECT_ORDER_CANCEL,
                    RabbitConst.ROUTING_ORDER_CANCEL,
                    orderId,
                    RabbitConst.DELAY_TIME);

            // 4、调用远程接口清空购物车（如果保存订单成功）
            cartFeignClient.clearCheckedCartItem(userId);

            // 5、返回订单 id
            return Result.ok(orderId);
        } catch (Exception e) {
            e.printStackTrace();

            return Result.<Long>fail().message("保存订单失败，购物车数据保留。");
        }
    }

    @GetMapping("/get/order/info/{orderId}")
    public Result<OrderInfo> getOrderInfoById(@PathVariable("orderId") Long orderId) {

        OrderInfo orderInfo = orderService.getOrderInfoById(orderId);

        return Result.ok(orderInfo);
    }

    @PostMapping("/orderSplit")
    public String orderSplit(
            @RequestParam("orderId") Long orderId,
            @RequestParam("wareSkuMap") String wareSkuMap) {

        return orderService.doOrderSplit(orderId, wareSkuMap);
    }

    @PostMapping("/submit/sec/kill/order")
    public Result<Long> submitSeckillOrder(@RequestBody OrderInfo orderInfo) {

        Long orderId = orderService.saveOrder(orderInfo, ProcessStatus.UNPAID);

        rabbitService.sendDelayMessage(
                RabbitConst.EXCHANGE_DIRECT_ORDER_CANCEL,
                RabbitConst.ROUTING_ORDER_CANCEL,
                orderId,
                RedisConst.SECKILL__TIMEOUT);

        return Result.ok(orderId);
    }
}
